﻿using System;
using System.Linq.Expressions;

namespace Postnuclear.Dependency.Visitors
  {
  /// <summary>
  /// Implements the visitor pattern to iterate through the expression tree.
  /// Takes the expression and replace method calls with <see cref="IValueProvider{T}"/> instances.
  /// </summary>
  internal sealed class ReplaceMethodCallsExpressionVisitor
    : ExpressionVisitor
    {
    #region Protected

    // Methods

    /// <summary>
    /// Visits the children of the <see cref="T:System.Linq.Expressions.MemberExpression"/>.
    /// </summary>
    /// <param name="node">The expression to visit.</param>
    /// <returns>
    /// The modified expression, if it or any subexpression was modified; otherwise, returns the original expression.
    /// </returns>
    protected override Expression VisitMember(MemberExpression node)
      {
      if (node.Expression == null || node.Expression.NodeType != ExpressionType.Call)
        {
        return base.VisitMember(node);
        }

      Func<Expression, Expression> handler = ReplaceMethodCall<object>;
      Expression replacement = handler.Method.ToGeneric(node.Expression.Type).Invoke<Expression>(null, node.Expression);
      return Expression.MakeMemberAccess(replacement, node.Member);
      }

    #endregion

    #region Private

    private static Expression ReplaceMethodCall<TValue>(Expression expression)
      {
      IValueProvider<TValue> valueProvider = Expression.Lambda<Func<TValue>>(expression).Observe();
      Expression<Func<TValue>> valueProviderExpression = () => valueProvider.Value;
      return valueProviderExpression.Body;
      }

    #endregion
    }
  }